#ifndef SCENEEDITOR_H
#define SCENEEDITOR_H

#include <QGLWidget>

#include <gfx/idevice.h>
#include <gfx/gl20/renderloop.h>
#include <gfx/mesh.h>
#include <gfx/camera.h>
#include <gfx/projector.h>
#include <math/ray.h>
#include <scene/scene.h>
#include <utils/transformer.h>
#include <physics/physicsworld.h>
#include <transformationeditdialog.h>
#include <scene_editor/transformerhandle.h>
#include <iwindow.h>
#include <QMap>
#include <QList>
#include <QComboBox>

class SceneEditorWidget : public QGLWidget,
                          public iWindow
{
  Q_OBJECT
  CE_OBJECT
public:
  enum CameraMode
  {
    CameraMode_Idle,
    CameraMode_Active,
    CameraMode_Rotate,
    CameraMode_Zoom,
    CameraMode_Translate
  };

private:

  enum KeyState
  {
    Key_Ctrl    = 0x01,
    Key_Alt     = 0x02,
    Key_Shift   = 0x04
  };

  enum ButtonState
  {
    Button_Left   = 0x01,
    Button_Right  = 0x02,
    Button_Middle = 0x04
  };

  enum SelectMode
  {
    Select_Replace,
    Select_Add,
    Select_Sub
  };

  enum HandleMode
  {
    HandleMode_Idle,
    HandleMode_Camera,
    HandleMode_Transformation
  };

  unsigned        _keys;
  unsigned        _buttons;

  bool Button (ButtonState state) const { return (_buttons & state) == state; }
  bool Key (KeyState state) const { return (_keys & state) == state; }

	ceRenderLoop*		_renderLoop;
	ceScene*				_scene;

  ceMaterial*     _boundingBoxMaterial;

  ceSector*       _controlerSector;

  cmPhysicsWorld*                 _physicsWorld;
  QList<btRigidBody*>             _physicBodies;
  QMap<iMesh*, btCollisionShape*> _physicShapes;

  std::vector<ceNode*>            _selectedNodes;
  ceMesh                          *_boundingBoxMesh;
  ceEntityNode                    *_boundingBoxNode;
  bool                            _boundingBoxNodeVisible;

  TransformationEditDialog        *_transformationEditor;

  TransformerCoordinates          _transformerCoordinates;
  Transformer                     *_transformerHandle;
  ceTransformer                   _transformer;

  /**
   * \name Camera transformation stuff
   * @{
   */
  float           _rotH;
  float           _rotV;
  float           _dist;
  ceVector3f      _spot;
  ceMatrix4f      _cameraMatrix;
  ceCamera				_camera;
  ceProjector			_projector;
  /** @} */


  /**
   * \name Input helpers
   * @{
   */
  bool            _addSelection;
  int             _prevX;
  int             _prevY;
  int             _leftMousePressPositionX;
  int             _leftMousePressPositionY;
  bool            _selectionMode;
  int             _selectionH0;
  int             _selectionV0;
  HandleMode      _handleMode;
  /** @} */

  void updateCamera ();
  void handleCameraRotate (int relx, int rely);
  void handleCameraTranslate (int relx, int rely);
  void handleCameraZoom (int relx, int rely);

  void ClearTransformationHandler ();
  void CreateTranslationHandler ();
  void CreateRotationHandle ();
  void UpdateTransformationHandlerMatrix ();

  void CreateBoundingBoxMaterial ();
  void AddPhysicsSector (ceSector* sector);
  void AddPhysicsNode (ceNode* node);

  ceNode* GetSelectableNode (ceNode* node) const;
  void UpdateBoundingBoxSelection ();
  void CreateOrUpdateBoundingBoxMesh(const ceBoundingBox &bbox);
  void DoPointSelection (int h, int v, SelectMode mode);
  void DoPointSelection (float h, float v, SelectMode mode);


  bool TestTransformerHandle (int h, int v);
  bool TestTransformerHandle (float h, float v);

  void DoTransformation (int h, int v);
  void DoTransformation (float h0, float v0, float h1, float v1);

	void CenterCameraOnTransformation ();

  void Grab (bool grab);

private slots:
  void customContextMenuRequested (const QPoint &pos);

protected:
  void keyPressEvent(QKeyEvent *event);
  void keyReleaseEvent(QKeyEvent *event);
  void mousePressEvent(QMouseEvent *event);
  void mouseReleaseEvent(QMouseEvent *event);
  void mouseMoveEvent(QMouseEvent *event);

public:
  SceneEditorWidget(QWidget *parent = 0);

  TransformationEditDialog *GetTransformationEditor ();

  ceScene* CreateNewScene ();
  void SetScene (ceScene* scene);
  ceScene* GetScene ();
  const ceScene* GetScene () const;

  ceSector* CreateControllerSector ();
  void SetControllerSector (ceSector* sector);
  ceSector* GetControllerSector ();
  const ceSector* GetControllerSector () const;

  cmPhysicsWorld* CreatePhysicsWorld ();
  cmPhysicsWorld* GetPhysicsWorld ();
  const cmPhysicsWorld* GetPhysicsWorld () const;
  cmPhysicsWorld* ClearPhysicsWorld (bool removeShapes = false);

  bool IsCameraIdle () const;

  /**
   * @brief InitializePhysics
   *
   * If no sector is given all sectors within the scene
   * except the controler sector are updated.
   *
   * @param sector By setting this sector only the nodes
   *               from within this sector are backed by
   *               the physics engine.
   */
  void InitializePhysics (ceSector* sector = 0);
  ceNode* GetEntity(const ceVector3f &from, const ceVector3f &at) const;

  ceMaterial* GetBoundingBoxMaterial ();

  const ceCamera& GetCamera () const;
  const ceProjector& GetProjector () const;

  virtual bool IsFullscreen () const;
  virtual ceWindowHandle GetWindowHandle ();

  virtual int GetX () const;
  virtual int GetY () const;

  virtual unsigned GetWidth () const;
  virtual unsigned GetHeight () const;

  bool MakeGLContextCurrent ();

  iDevice* GetDevice ();

  /**
   * \brief Returns a ray from the current viewport of the camera.
   *
   * \param x X-Coordinate within screen coordinates.
   * \param y Y-Coordinate within screen coordinates.
   *
   * \return The ray from the camera into the pointing direction
   */
  ceRay GetRay (int x, int y) const;

  /**
   * \name Selection
   * @{
   */
  void ClearSelection ();
  void SetSelectedNode (ceNode* node);
  void AddSelectedNode (ceNode* node);
  void RemoveSelectedNode (ceNode* node);
  ceNode* GetSelectedNode ();
  /**
   * @}
   */

public slots:
  void TransformationChanged ();

  void SetTransformerCoordinates (TransformerCoordinates coordinates);
  void SetTransformerMode (TransformerMode mode);

public:
	void initializeGL ();
	void resizeGL(int w, int h);
	void paintGL();

signals:
  void Changed ();
  void MouseMoved (float h, float v);
  void PointSelection (float h, float v);
  void BeginAreaSelection (float h, float v);
  void EndAreaSelection (float h, float v);
  void AreaSelection (float h0, float v0, float h1, float v1);
  void NodeSelected (ceNode* node);
  void NodeDeselected (ceNode* node);
  void NodeSelectionChanged ();
};

class SceneEditorComponent : public QWidget
{
  Q_OBJECT
private:
  SceneEditorWidget*    _editor;

  QButtonGroup*         _transformGroup;
  QPushButton*          _buttonTranslate;
  QPushButton*          _buttonRotate;

  QButtonGroup*         _coordinateGroup;
  QPushButton*          _buttonLocal;
  QPushButton*          _buttonGlobal;

  QComboBox*            _comboCoordinates;

private slots:
  void on_transfromGroup_buttonClicked (QAbstractButton* button);
  void on_comboCoordinates_currentIndexChanged (int idx);

public:
  SceneEditorComponent (QWidget* parent = 0);

  SceneEditorWidget*  GetEditor ();
};


#endif // SCENEEDITOR_H
